/*---------------------------------------------------------------
 * Confidential and Proprietary
 * Copyright 2006 CMO & Hewlett-Packard Development Company, L.P.
 * All Rights Reserved
 *---------------------------------------------------------------
 * Project Name    : SPF
 * Sub Project Name: SPF
 *
 * Class Name      : RestrictConcurrentFunctionAccessor
 * Purpose         : 
 *---------------------------------------------------------------
 * Modification Log
 * Date         Ver. #      Programmer       Description
 * 
 */
package com.erdos.common.util;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.erdos.common.exception.ServiceException;


@SuppressWarnings("unchecked")
public class RestrictConcurrentFunctionAccessor {
	private static final Log logger=LogFactory.getLog(RestrictConcurrentFunctionAccessor.class);
	/*
	 * cache key for the in use functions, cached content is a MAP
	 */
	private static final String IN_USE_FUNCTION="INUSEFUNCTION";
	private static final String UNKNOWN_USER="UNKNOWN_USER";
	public static final String SEPARATOR="#";
	
	/*
	 * static block to initialize the data.
	 */
	static {
		try{
		refreshRestrictConcurrentFunction();
		CacheManager.putInCache(IN_USE_FUNCTION, new HashMap());
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}
	
	/**
	 * returns <code>true</code> if the specified function is a concurrent access 
	 * restricted function.
	 * @param function to be checked function
	 * @return <code>true</code> if yes, <code>false</code> otherwise
	 */
	@SuppressWarnings("unchecked")
	public static boolean isRestrictConcurrentFunction(String function) {
		if(function==null) return false;
		Collection restrictFuncs=(Collection)CacheManager.getFromCache(AppConstants.RESTRICT_CONCURRENT_FUNCTION);
		boolean included=restrictFuncs.contains(function);
		logger.debug("function " + function + " is in restricted function set:" + included);
		return included;
	}
	
	/**
	 * binds the specified function to the specified session/user.
	 * @param function to be bound function
	 * @param session/user to whom the function will to be bound
	 * @return <code>true</code> if success, <code>false</code> otherwise
	 * @throws IllegalAccessException if the function already bound to another session/user
	 */
	public static boolean addToControl(String function, String session) throws IllegalAccessException{
		logger.debug("addToControl: function=" + function + "; session=" +session);
		
		if(session==null || function==null) return false;
		int sep=function.indexOf(SEPARATOR);
		String prefix=sep==-1?function:function.substring(0, sep);
		if(!isRestrictConcurrentFunction(prefix)) return false; 
		
		Map isUse=(Map)CacheManager.getFromCache(IN_USE_FUNCTION);
		
		if(!isUse.containsKey(function)) {
			isUse.put(function,session);
		} else {
			if(!isUse.get(function).equals(session)) {
				logger.warn("function " + function + " is in use by " + isUse.get(function));
				throw new IllegalAccessException("this function is in use by " + isUse.get(function));
			}  else {
				logger.warn("function " + function + " is in use by the same user." + session);
				throw new IllegalAccessException("this function is in use by same user " + session);
			}			
		}
		
		Collection funcs=(Collection)CacheManager.getFromCache(session);
		if(funcs==null) {				
			funcs=new HashSet();
		}
		funcs.add(function);			
		CacheManager.putInCache(session,funcs);	
		
		logger.info("function " + function + " added to session " + session);			
		return true;		
	}
	
	/**
	 * binds the specified function to the fixed session/user.
	 * @param function function to be bound function
	 * @return <code>true</code> if success, <code>false</code> otherwise
	 * @throws IllegalAccessException if the function already bound to another session/user
	 */
	public static boolean addToControl(String function) throws IllegalAccessException {		
		return addToControl(function, UNKNOWN_USER);
	}
	
	/**
	 * unbinds the specified function from the specified session/user.
	 * @param session session/user from whom function to be unbound
	 * @param function function to be unbound
	 * @return <code>true</code> if success, <code>false</code> otherwise
	 */
	public static boolean removeFromControl(String session, String function) {
		if(function==null || session==null) return false;
		
		Map inUse=(Map)CacheManager.getFromCache(IN_USE_FUNCTION);
		inUse.remove(function);
		Collection funcs=(Collection)CacheManager.getFromCache(session);
		if(funcs!=null && funcs.contains(function)) {
			funcs.remove(function);
			logger.info("function "+ function + " removed from session controller.");
			return true;
		}
		
		return false;
	}
	
	/**
	 * unbinds all the functions bound to the specified session/user.
	 * @param session to be removed session/user
	 * @return <code>true</code> if success, <code>false</code> otherwise
	 */
	public static boolean removeFromControl(String session) {
		if(session==null) return false;
		Collection funcs=(Collection)CacheManager.getFromCache(session);
		if(funcs==null || funcs.isEmpty()) return false;
		Map inUse=(Map)CacheManager.getFromCache(IN_USE_FUNCTION);
		Iterator iter=funcs.iterator();
		while(iter.hasNext()) {
			inUse.remove(iter.next());
		}
		funcs.clear();
		CacheManager.removeEntry(session);
		flushIfNecessary(session);
		logger.info("session removed from controller:" + session);
		return true;
	}
	
	/**
	 * unbinds the specified function from the fixed session/user.
	 * @param function function to be unbound
	 * @return <code>true</code> if success, <code>false</code> otherwise
	 */
	public static boolean removeFunctionFromControl(String function) {
		return removeFromControl(UNKNOWN_USER, function);
	}
	
	/**
	 * returns <code>true</code> if the specified function is in use,
	 * <code>false</code> otherwise.
	 * @param function to be checked function
	 * @return <code>true</code> if the specified function is in use, 
	 * <code>false</code> otherwise
	 */
	public static boolean isFunctionInUse(String function) {
        return ((Map)CacheManager.getFromCache(IN_USE_FUNCTION)).containsKey(function);
	}
	
	/**
	 * flushes the specified key if necessary.
	 * @param session to be flushed cache key
	 */
	private static void flushIfNecessary(String session) {
		CacheManager.flushEntry(session);
//		CacheManager.flushEntry(IN_USE_FUNCTION);
	}
	
	/**
	 * TODO
	 * @throws ServiceException
	 * @throws Exception
	 */
	public static void refreshRestrictConcurrentFunction() throws ServiceException, Exception {
/*		Collection restrictFuncs=ReferenceTableDetailCache.
			getReferenceTableDetailsByReferenceTableId(AppConstants.RESTRICT_CONCURRENT_FUNCTION);
		
		Set funcIds=new HashSet();
		Iterator iter=restrictFuncs.iterator();
		while(iter.hasNext()) {
			funcIds.add(((ReferenceTableDetail)iter.next()).getData1Value());
		}
		restrictFuncs.clear();
		logger.debug("configured concurrent access restricted functions:" + funcIds);
		
		CacheManager.putInCache(AppConstants.RESTRICT_CONCURRENT_FUNCTION, funcIds);*/
	}
}
